import json

from django.db import transaction
from django.shortcuts import render
from rest_framework import serializers
from django_celery_beat.models import (
    CrontabSchedule,  # CrontabSchedule 定时任务 模型
    PeriodicTask  # PeriodicTask 周期任务 模型
)
# from rest_framework.exceptions import ValidationError

from apps.cronjob.models import CronJob
from utils.json_response import ErrorResponse
from utils.serializers import CustomModelSerializer
from utils.viewset import CustomModelViewSet


# Create your views here.


class CronJobSerializer(CustomModelSerializer):
    """
    定时任务 序列化器
    """
    task_name = serializers.StringRelatedField(read_only=True, source='task.name')
    env_name = serializers.StringRelatedField(read_only=True, source='env.name')

    class Meta:
        model = CronJob
        fields = '__all__'


class CronJobView(CustomModelViewSet):
    """
    定时任务 视图
    """

    queryset = CronJob.objects.all()
    serializer_class = CronJobSerializer
    # permission_classes = [permissions.IsAuthenticated]
    denied_methods = ['retrieve']
    filter_fields = ('project', 'task')

    def create(self, request, *args, **kwargs):
        """
        定时任务 创建
        """
        with transaction.atomic():  # 1、开启事务
            save_point = transaction.savepoint()  # 1、创建保存点
            try:
                res = super().create(request, *args, **kwargs)
                # 1、获取创建的定时任务对象
                cron_job: CronJob = self.queryset.get(id=res.data['data']['id'])
                # 2、在django-celery-beat这个应用的周期任务表中添加一条定时任务
                cron = self.create_crontab(rule_str=res.data['data']['rule'])
                # 创建一个周期任务
                self.create_periodic_task(cron_job=cron_job, cron=cron, request=request)
            except:
                # 3、如果创建失败，则回滚事务
                transaction.savepoint_rollback(save_point)
                # '创建失败'
                return ErrorResponse(msg='定时任务创建失败！', code=500)
            else:
                # 4、如果创建成功，则提交事务
                transaction.savepoint_commit(save_point)
                return res

    def update(self, request, *args, **kwargs):
        with transaction.atomic():
            save_point = transaction.savepoint()  # 1、创建保存点
            try:
                res = super().update(request, *args, **kwargs)
                # 获取定时任务对象
                cron_job: CronJob = self.queryset.get(id=res.data['data'].get('id'))
                # 获取周期规则
                cron: CrontabSchedule = self.create_crontab(res.data['data']['rule'])
                # 更新周期任务
                self.update_periodic_task(cron_job, cron, request)
            except Exception as e:
                transaction.savepoint_rollback(save_point)  # 2、回滚事务
                return ErrorResponse(msg=f'修改失败，失败原因: {e.args[0]}', code=500)
            else:
                transaction.savepoint_commit(save_point)  # 3、提交事务
                return res

    def destroy(self, request, *args, **kwargs):
        """删除定时任务"""
        with transaction.atomic():
            save_point = transaction.savepoint()
            try:
                tasks = PeriodicTask.objects.filter(name=kwargs.get('pk'))
                if not tasks.exists():
                    raise Exception('周期任务id不存在')
                per_task = tasks.first()
                # per_task = PeriodicTask.objects.get(name=kwargs.get('pk'))
                per_task.enabled = False
                per_task.delete()
                res = super().destroy(request, *args, **kwargs)
            except Exception as e:
                transaction.savepoint_rollback(save_point)
                # return res.ValidationError('删除定时任务失败')
                return ErrorResponse(msg=f'删除定时任务失败,失败原因: {e.args[0]}', code=5000)
            else:
                transaction.savepoint_commit(save_point)
                return res

    @staticmethod
    def create_crontab(rule_str) -> CrontabSchedule:
        """创建周期规则"""
        rule_dict = dict(zip(
            ['minute', 'hour', 'day_of_week', 'day_of_month', 'month_of_year'],
            rule_str.split(" "))
        )
        try:
            print(f'获取周期规则:{rule_dict}')
            return CrontabSchedule.objects.get(**rule_dict)
        except:
            print(f'创建周期规则:{rule_dict}')
            return CrontabSchedule.objects.create(**rule_dict)

    @staticmethod
    def create_periodic_task(cron_job, cron, request):
        """创建周期任务"""
        try:
            PeriodicTask.objects.create(
                name=cron_job.id,
                task='run.tasks.run_cron',
                crontab=cron,
                kwargs=json.dumps({
                    'plan_id': request.data.get('plan'),
                    'env_id': request.data.get('env'),
                    'tester': request.user.username,
                }),
                enabled=cron_job.status,
            )
        except Exception:
            raise Exception('创建周期任务失败')

    @staticmethod
    def update_periodic_task(cron_job, cron, request):
        """修改任务状态"""
        tasks = PeriodicTask.objects.filter(name=cron_job.id)  # 获取周期任务
        if tasks.exists():
            per_task = tasks.first()
            # per_task = PeriodicTask.objects.get(name=cron_job.id)   # 获取周期任务
            per_task.kwargs = json.dumps({
                'plan_id': request.data.get('plan'),
                'env_id': request.data.get('env'),
                'tester': request.user.username,
            }),
            per_task.crontab = cron
            per_task.enabled = cron_job.status
            per_task.save()
        else:
            raise Exception('周期任务id不存在')
